package ace.cmp.data.spring.data.jdbc.querydsl.rsql.core.manager.impl;

import ace.cmp.data.spring.data.jdbc.querydsl.rsql.core.manager.Manager;
import ace.cmp.data.spring.data.jdbc.querydsl.rsql.core.manager.ManagerConstructArgs;
import ace.cmp.data.spring.data.jdbc.querydsl.rsql.core.manager.ManagerFactory;
import ace.cmp.id.generator.api.IdGenerator;
import ace.cmp.spring.data.jdbc.querydsl.core.QuerydslRelationHelper;
import ace.cmp.spring.data.jdbc.querydsl.core.repository.QuerydslJdbcBaseRepository;
import ace.cmp.spring.data.jdbc.querydsl.core.util.QuerydslAnnotationUtils;
import ace.cmp.spring.data.jdbc.querydsl.rsql.core.RsqlQuerydslParser;
import ace.cmp.spring.data.jdbc.querydsl.rsql.core.RsqlQuerydslVisitor;
import ace.cmp.spring.util.core.SpringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.ResolvableType;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;

/**
 * @author caspar
 * @date 2023/9/20 17:21
 */
@Slf4j
public class ManagerFactoryImpl implements ManagerFactory {

  private final SpringUtils springUtils;

  public ManagerFactoryImpl(SpringUtils springUtils) {
    this.springUtils = springUtils;
  }

  /**
   * {@link DtoManagerImpl#DtoManagerImpl}
   *
   * @param args
   * @return
   */
  @Override
  public <EntityT, IdT> Manager<EntityT, IdT> getOrCreate(ManagerConstructArgs<EntityT, IdT> args) {
    Class<EntityT> entityClass = args.getEntityClass();
    String beanName = StringUtils.uncapitalize(entityClass.getSimpleName()) + "Manager";
    if (springUtils.getDefaultListableBeanFactory().containsBean(beanName)) {
      log.info("already exist beanName[{}] entityClass[{}]", beanName, entityClass);
      return (Manager) springUtils.getApplicationContext().getBean(beanName);
    }
    log.info("register bean beanName[{}] entityClass[{}] success", beanName, entityClass);
    QuerydslJdbcBaseRepository<EntityT, IdT> repository = this.getRepository(args);
    IdGenerator<IdT> idGenerator = this.getIdGenerator(args);
    QuerydslRelationHelper querydslRelationHelper = this.getQuerydslRelationHelper();
    RsqlQuerydslParser rsqlQuerydslParser = springUtils.getDefaultListableBeanFactory().getBean(RsqlQuerydslParser.class);
    RsqlQuerydslVisitor rsqlQuerydslVisitor = new RsqlQuerydslVisitor(args.getEntityClass());
    QuerydslAnnotationUtils querydslAnnotationUtils = springUtils.getDefaultListableBeanFactory().getBean(QuerydslAnnotationUtils.class);
    RelationalMappingContext relationalMappingContext = springUtils.getDefaultListableBeanFactory().getBean(RelationalMappingContext.class);

    ResolvableType resolvableType = ResolvableType.forClassWithGenerics(
        Manager.class, args.getEntityClass()
    );

    springUtils.registerRootBean(
        beanName,
        resolvableType,
        () -> {
          return new ManagerImpl<>(
              args.getIdClass(),
              args.getEntityClass(),
              repository,
              idGenerator,
              querydslRelationHelper,
              rsqlQuerydslParser,
              rsqlQuerydslVisitor,
              querydslAnnotationUtils,
              relationalMappingContext);
        },
        beanDefinitionBuilder -> {
        });

    Manager manager = (Manager) springUtils.getApplicationContext().getBean(beanName);
    return manager;
  }

  private QuerydslRelationHelper getQuerydslRelationHelper() {
    return springUtils.getDefaultListableBeanFactory().getBean(QuerydslRelationHelper.class);
  }

  private <TId> IdGenerator<TId> getIdGenerator(ManagerConstructArgs args) {
    return springUtils.getBeanByClassWithGenerics(IdGenerator.class, args.getIdClass());
  }

  private <EntityT, IdT> QuerydslJdbcBaseRepository<EntityT, IdT> getRepository(ManagerConstructArgs args) {
    return springUtils.getBeanByClassWithGenerics(QuerydslJdbcBaseRepository.class, args.getEntityClass(), args.getIdClass());
  }
}
